// 参考链接： https://www.webhek.com/post/you-do-not-need-jquery/
(function(global,factory){
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global = global || self, global.Dom = factory());
}(this,function(){
    const matches = function(){
        if(!Element.prototype.matches){
            Element.prototype.matches = 
            Element.prototype.matchesSelector || 
            Element.prototype.mozMatchesSelector ||
            Element.prototype.msMatchesSelector || 
            Element.prototype.oMatchesSelector || 
            Element.prototype.webkitMatchesSelector ||
            function(s) {
                let matches = (this.document || this.ownerDocument).querySelectorAll(s),
                    i = matches.length;
                while (--i >= 0 && matches.item(i) !== this) {}
                return i > -1;            
            };
        }
        return Element.prototype.matches;
    }();

    class Dom{

        //伪数组转为真数值
        static arrayLike(arrLike){
            return Array.from ? Array.from(arrLike) : Array.prototype.slice.call(arrLike);
        }

        constructor(){
            
        }

        static isSurport(el){
            return 'content' in el;
        }

        static isDOM(el){
            let nodeTypes = [Node.ELEMENT_NODE,Node.DOCUMENT_NODE];
            return nodeTypes.includes(el.nodeType);
        }

        static parse(htmlString){
            const range = document.createRange();
            return range.createContextualFragment(htmlString);
        }

        static seniorParse(htmlString){
            const fragment = document.createDocumentFragment()
            , tmpEl = document.createElement('div');
            tmpEl.innerHTML = htmlString;
            while (tmpEl.firstElementChild){
                fragment.appendChild(tmpEl.firstElementChild)
            }
            return fragment;
        }

        static replaceWith(target,htmlString){
            Array.prototype.forEach.call(document.querySelectorAll(target),(el) => {
                let docFragement = Router.seniorParse(htmlString)
                el.parentNode.insertBefore(docFragement, el);
                el.parentNode.removeChild(el);
            });
        }

        static getEl(selector){
            let elTypes = [Node.ELEMENT_NODE,Node.DOCUMENT_NODE];
            if(elTypes.includes(selector.nodeType)){
                return selector;
            }else {
                if(/^#[^#\s\b]+$/.test(selector)){
                    selector = document.querySelector(selector)
                }else {
                    selector = document.querySelectorAll(selector)
                }
            }
            return selector;
        }

        static queryAll(selector,oParent){
            return  oParent ? oParent.querySelectorAll(selector) : document.querySelectorAll(selector)
        }

        static attr(el,htmlAttr,val){
            if(!val && typeof htmlAttr == 'string'){
                return el.getAttribute(htmlAttr)
            }
            if(!val && Dom.isPlainObject(htmlAttr)){
                for(let htmlKey in htmlAttr){
                    val = htmlAttr[htmlKey]
                    el.setAttribute(htmlKey,val)
                }
            }else {
                el.setAttribute(htmlAttr,val)
            }
        }

        static css(el,cssAttr,val,unit=''){
            //获取属性值
            if(!val && typeof cssAttr == 'string'){
                return el.currentStyle ? el.currentStyle[cssAttr] : getComputedStyle(el,false)[cssAttr];
            }
            let setUnit = function (val) {
                //如果只是数字或小数
                if(/^(\d?.)?(\d+)?$/.test(val)){
                    unit = 'px'
                }
            }
            //以对象的方式传值
            if(!val && Dom.isPlainObject(cssAttr)){
                for(let cssKey in cssAttr){
                    val = cssAttr[cssKey]
                    setUnit(val)
                    el.style.cssText += ';' +(cssKey + ':' + val+unit)
                }
            }else{
                setUnit(val)
                el.style.cssText += ';' +(cssAttr + ':' + val+unit)
            }
        }

        static getRect(el){
            let rect = el.getBoundingClientRect()
            ,data = {
                left:rect.left
                ,top:rect.top
                ,width:el.offsetWidth
                ,height:el.height
            };
            return function(attr){
                return data[attr];
            }
        }

        // el必须是已定位元素，相对于最近已定位的祖先元素的偏移量
        static position(el,attr){
            return {
                left:el.offsetLeft,
                top:el.offsetTop,
                width:el.offsetWidth,
                height:el.offsetHeight
            }
        }

        static isVisible(el){
            let style= el.style;
            if(style.display == 'none'){
                return false
            }
            if(style.visibility == 'collapse'|| style.visibility == 'hidden'){
                return false;
            }
            if(!style.border){
                if(parseInt(Dom.css(el,'width')) ==0  || parseInt(Dom.css(el,'height')) == 0){
                    return false;
                }
            }
            //元素是否在屏幕窗口范围内
            let winClientWidth = document.documentElement.clientWidth;
            let winClientHeight = document.documentElement.clientHeight;
            let rect = Dom.offset(el);
            if(!(rect.l > 0 && rect.l < winClientWidth) || !(rect.t > 0 && rect.t < winClientHeight)){
                return false;
            }
            return true;
        }

        static hide(el){
            let execBody = (el)=>{
                Dom.css(el,'display','none');
            }
            if(Dom.isDOM(el)){
                execBody(el)
            }else{
                Dom.arrayLike(el).forEach(ele=>{
                    execBody(ele);
                })
            }
        }

        static show(el){
            let execBody = (el)=>{
                this.css(el,'display','block');
            }
            if(Dom.isDOM(el)) {
                execBody(el)
            }else{
                Dom.arrayLike(el).forEach(ele=>{
                    execBody(ele);
                })
            }
        }

        static addClass(el,className){
            className = className.trim().replace(/\s+/g,' ').split(' ');
            el.classList.add.apply(el.classList,className)
        }

        static removeClass(el,className){
            className = className.trim().replace(/\s+/g,' ').split(' ');
            el.classList.remove.apply(el.classList,className)
        }

        static hasClass(el,className) {
            return el.classList.contains(className)
        }

        static removeDOM(selector){
            selector = selector.nodeType == Node.ELEMENT_NODE ? [selector] : document.querySelectorAll(selector)
            Array.from(selector).forEach(function(el){
                el.parentNode.removeChild(el)
            })
        }

        static append(targetEl,html){
            if(typeof html == 'string'){
                //创建html片段流，fragment存放在内存中
                //将节点全部先追加到html片段流中，最后将html片段流一次性追加至html文档中
                //而不是一个一个节点的追加到html文档中
                let fragment = document.createDocumentFragment();
                let tempEl = document.createElement('div');
                tempEl.innerHTML = html;
                //firstElementChild只包含元素节点本身
                //firstChild,包含 元素节点+文本节点+注解
                while (tempEl.firstElementChild){
                    fragment.appendChild(tempEl.firstElementChild)
                }
                let childNodes = Object.assign({},fragment.childNodes);
                childNodes.length = Object.keys(childNodes).length;
                targetEl.appendChild(fragment);
                return childNodes;
            }else{
                return targetEl.appendChild(html);
            }
        }

        static insertAfter(newNode,targetNode){
            let parent = targetNode.parentNode;
            // 最好不要用lastChild，因为lastChild 可以获取最后一个文本节点，注释节点，元素节点
            if(parent.lastElementChild == targetNode){
                //向指定父容器中追加子元素
                parent.appendChild(newNode)
            }else{
                //在指定父容器中，将newNode追加至targetNode之前
                parent.innserBefore(newNode,targetNode.nextElementSibling);
            }
        }

        static nextSibling(el){
            return el.nextElementSibling;
        }

        static prevSibling(el){
            return el.previousElementSibling;
        }

        static siblings(el){
            return Array.prototype.filter.call(el.parentNode.children, child=>child !== el);
        }
        
        static closest(el,selector){
            if(!selector){return el.parentElement;}
            while(el){
                if (Element.prototype.matches.call(el, selector)) {
                    return el;
                }else{
                    el = el.parentElement;
                }
            }
            return el;
        }

        static closest2(el, selector){
            if(!Dom.isDOM(el)){return;}
            let els, targetEl
            ,isSame = (els, el)=>{
                let len = els.length;
                for(let i = 0; i<len; i++){
                    if(els[i].isSameNode(el)){
                        return els[i];
                    }
                }
                return false;
            }
            ,traversal = (el, selector)=>{
                els = Dom.queryAll(el.parentNode, selector);
                targetEl = isSame(els, el);
                while(!targetEl){
                    el = el.parentNode;
                    //判断节点是否是html根节点
                    if(el && el.nodeType == el.DOCUMENT_NODE){
                        return false;
                    }
                    traversal(el, selector);
                }
                return targetEl;
            };
            return traversal(el, selector);
        }

        static clone(el){
            return el.cloneNode(true);
        }

        static on(par,event,selector,handle){
            par = (par.nodeType == Node.ELEMENT_NODE || par.nodeType == Node.DOCUMENT_NODE)? par: document.querySelectorAll(par);
            const contains = function(target,otherNode){
                for(let i=0,len=otherNode.length; i<len; i++){
                    let node = otherNode[i];
                    if(target.contains(node)){
                        return node;
                    }
                }
                return false;
            }
            par.addEventListener(event,function(e){
                let node = ''
                let targetEls = Array.from(par.querySelectorAll(selector))
                if(node = contains(e.target,targetEls)){
                    return handle.call(node,e)
                }
            })
        }

        static animationEnd(ele,type,sucHandle){
            type = type || 'animation'
            //1、获取动画名
            let eventName = function(){
                const animateEvtNames = {
                    'animation': 'animationend',
                    'OAnimation': 'oAnimationEnd',
                    'MozAnimation': 'mozAnimationEnd',
                    'WebkitAnimation': 'webkitAnimationEnd'
                }
                const transitEvtNames = {
                    //chrome
                    WebkitTransition : 'webkitTransitionEnd',
                    //Firefox
                    MozTransition    : 'transitionend',
                    //Opera
                    OTransition      : 'oTransitionEnd otransitionend',
                    transition       : 'transitionend'
                }
                const animateName = (function(el) {
                    let animation = type == 'animation' ? animateEvtNames : transitEvtNames;
                    for (let t in animation) {
                        if (el.style[t] !== undefined) {
                            return animation[t];
                        }
                    }
                })(document.createElement('div'));
                return animateName;
            }();
            //2、给ele绑定动画结束的回调
            const animateEndEventListener = function(sucHandle){
                //在每次transitionEnd的事件后执行该函数
                const handleAniEnd = function(e){
                    ele.removeEventListener(animateName,handleAniEnd);
                    sucHandle.call(this,e,ele);
                };
                ele.addEventListener(animateName,handleAniEnd,false);
            }
            eventName && animateEndEventListener(sucHandle)
        }

        static evtListener(el,event,handle){
            el = typeof el == 'object' && !(el instanceof Array) && el.length>0 ? [...el] : typeof el == 'string' ? [...Dom.queryAll(el)] : [el]
            el.forEach(el=>el.addEventListener(event,handle));
        }

        static removeListener(el,event,handle){
            el = Dom.isDOM(el) ? [...el] : [el];
            el.forEach(el=>el.removeEventListener(event,handle));
        }

        static isPlainObject(obj){
            if(!obj || typeof obj != 'object'){return false;}
            if(obj.constructor.prototype.hasOwnProperty("isPrototypeOf")){
                return true;
            }
            return false;
        }

        //深浅拷贝
        static extend(isDeep,target) {
            let self = this;
            let args = Array.from(arguments);
            if(typeof isDeep != 'boolean' || isDeep === false){
                return Object.assign.apply(Object,args);
            }
            let matchIns = function(targetVal,value) {
                let insObj = null;
                if(value instanceof RegExp){
                    insObj = new RegExp(value.source,value.flags);
                } else if(value instanceof Date){
                    insObj = new Date(value);
                } else if(value instanceof Array){
                    insObj = targetVal || [];
                } else {
                    insObj = targetVal || {};
                }
                return insObj;
            };
            let deepClone = function(target,obj) {
                Object.keys(obj).forEach(key => {
                    let value = obj[key];
                    if(typeof value == 'object' && value != null && !self.isDOM(value)){
                        let insObj = matchIns(target[key],value);
                        target[key] = deepClone(insObj,value);
                    } else {
                        target[key] = value;
                    }
                });
                return target;
            };
            args.slice(2).forEach(obj => {
                deepClone(target,obj);
            });
            return target;
        }
        //只拷贝目标对象中，存在的属性，不添加额外属性
        static coverOpts(target,...sources) {
            let matchIns = function(targetVal,value) {
                let insObj = null;
                if(value instanceof RegExp){
                    insObj = new RegExp(value.source,value.flags);
                } else if(value instanceof Date){
                    insObj = new Date(value);
                } else if(value instanceof Array){
                    insObj = targetVal || [];
                } else {
                    insObj = targetVal || {};
                }
                return insObj;
            };
            let coverKey = function(target,source) {
                Object.keys(source).forEach(key => {
                    //只覆盖目标对象中，存在的属性，不添加额外属性
                    if(target.hasOwnProperty(key)){
                        let value = source[key];
                        if(typeof value == 'object' && value != null && !self.isDOM(value)){
                            let insObj = matchIns(target[key],value);
                            target[key] = coverKey(insObj,value);
                        } else {
                            target[key] = value;
                        }
                    }
                });
                return target;
            };
            sources.forEach(source => {
                coverKey(target,source);
            });
            return target;
        }
        //拷贝排除，指定目标属性
        static cloneExclude(excludeKeys,target,...source) {
            let isDeep = false;
            typeof target == 'boolean' && (
                isDeep = true,
                target = source[0],
                source = source.slice(1)
            );
            if(typeof target == 'boolean'){
                target = this.common.extend(isDeep,target,...source);
            } else {
                target = Object.assign(target,...source);
            }
            excludeKeys.split(',').forEach(key => delete target[key]);
            return target;
        }

        static assign(target,[...settings]){
            settings.forEach(setting=>Object.assign(target,setting));
            return target
        }

        static createForm(options){
            options = Dom.assign({
                url:null
                ,type:'post'
                ,newWin:true
                ,data:null
            },options);
            let form = document.createElement("form");
            document.body.appendChild(form);
            form.action = options.url;
            form.method = options.type;
            form.target = options.newWin ? '_blank':'_self';
            let data = options.data;
            Array.isArray(data) && data.forEach(function(item,i) {
                let input = document.createElement("input");
                input.type = 'hidden';
                input.value = item.value;
                input.name = item.name;
                form.appendChild(input);
            })
            form.submit();
            form.remove();
        }

    }
    return Dom;
}));